﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using Kugar.Core.ExtMethod;
using System.ComponentModel;

namespace Kugar.ERPFramework.View.WinForm
{

    public abstract class FieldsManager : IEnumerable<KeyValuePair<string, FieldConfig>>,INotifyCollectionChanged
    {
        private Dictionary<string, FieldConfig> _cache = null;

        protected FieldsManager()
        {
            _cache = new Dictionary<string, FieldConfig>();
        }

        public FieldConfig this[string fieldID]
        {
            get { return _cache[fieldID]; }
        }

        public void SetValue(string fieldID, object value)
        {
            var field = _cache.TryGetValue(fieldID);

            if (field!=null)
            {
                field.Value = value;
            }
        }

        public object GetValue(string fieldID,object defaultValue=null)
        {
            var field = _cache.TryGetValue(fieldID);

            if (field != null)
            {
                return field.Value ;
            }
            else
            {
                return defaultValue;
            }
        }

        protected virtual bool OnAddField(FieldConfig config)
        {
            if (_cache.ContainsKey(config.ID))
            {
                throw new ArgumentOutOfRangeException("ID");
            }

            if (string.IsNullOrWhiteSpace(config.FieldName))
            {
                throw new ArgumentNullException("FieldName");
            }

            if (config.DataType == null)
            {
                throw new ArgumentNullException("DataType");
            }

            _cache.Add(config.ID, config);

            config.Manager = this;

            if (CollectionChanged!=null)
            {
                var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, config);
                CollectionChanged(this, config);
            }

            return true;

        }

        public virtual void RemoveField(string fieldID)
        {
            _cache.Remove(fieldID);
        }

        public virtual void BindObjectToField(object obj)
        {
            foreach (var fieldConfig in _cache)
            {
                if (fieldConfig.Value==null)
                {
                    continue;
                }
                fieldConfig.Value.BindingData = obj;
            }
        }

        public int Count
        {
            get { return _cache.Count; }
        }

        public IEnumerator<KeyValuePair<string,FieldConfig>> GetEnumerator()
        {
            return _cache.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public event NotifyCollectionChangedEventHandler CollectionChanged;
    }

    

    public abstract class FieldConfig
    {
        private object _bindingObj = null;

        public FieldConfig(string id, object bindingObj=null)
        {
            _bindingObj = bindingObj;
            ID = id;
        }

        public abstract object Value { set; get; }

        #region 公开属性
        public string FieldName { set; get; }

        public string ID { set; get; }

        private string _labelText = null;
        public string LabelText
        {
            set
            {
                if (_labelText != value)
                {
                    _labelText = value;

                    OnLabelTextChanged();
                }
            }
            get { return _labelText; }
        }


        private bool _visible = true;
        public bool Visible
        {
            get { return _visible; }
            set
            {
                _visible = value;
                OnVisibleChanged();
            }
        }

        private bool _enabled = true;
        public bool Enabled
        {
            get { return _enabled; }
            set
            {
                _enabled = value;
                OnEnabledChanged();
            }
        }

        public Type DataType { set; get; }

        public object BindingData
        {
            set
            {
                if (_bindingObj == value)
                {
                    return;
                }

                if (_bindingObj != null)
                {
                    if (_bindingObj is INotifyPropertyChanging)
                    {
                        ((INotifyPropertyChanging)_bindingObj).PropertyChanging -= OnBindingPropertyChanging;
                    }

                    if (_bindingObj is INotifyPropertyChanged)
                    {
                        ((INotifyPropertyChanged)_bindingObj).PropertyChanged -= OnBindingPropertyChanged;
                    }
                }

                if (value != null)
                {
                    if (value is INotifyPropertyChanging)
                    {
                        ((INotifyPropertyChanging)value).PropertyChanging += OnBindingPropertyChanging;
                    }

                    if (value is INotifyPropertyChanged)
                    {
                        ((INotifyPropertyChanged)value).PropertyChanged += OnBindingPropertyChanged;
                    }
                }

                _bindingObj = value;

                Value = OnGetBindObjectValue();

                OnBindingDataChanged();
            }
            get { return _bindingObj; }

        }

        private bool _isNullable=false;
        public bool IsNullable
        {
            set
            {
                if (_isNullable!=value)
                {
                    _isNullable = value;
                    OnIsNullableChanged();
                }
            }
            get { return _isNullable; }
        }

        public FieldsManager Manager { set; get; }

        #endregion

        #region "事件"

        public event EventHandler VisibleChanged;
        public event EventHandler EnabledChanged;
        public event EventHandler LabelTextChanged;
        public event EventHandler IsNullableChanged;
        public event EventHandler BindingDataChanged; 
        

        /// <summary>
        ///     如果当前值被修改,引发该事件,并且允许取消修改值的操作
        /// </summary>
        public event EventHandler<CancelEventArgs> ValueChanging;

        /// <summary>
        ///     当前字段值被修改完成后,引发该事件
        /// </summary>
        public event EventHandler ValueChanged;
        #endregion

        #region "事件引发函数"

        /// <summary>
        ///     字段值正在修改中,引发ValueChanging事件,返回true,表示允许修改,返回false,表示不允许修改
        /// </summary>
        /// <param name="newValue">修改中的新值</param>
        /// <param name="oldValue">修改前的值</param>
        /// <returns></returns>
        protected virtual bool OnValueChanging(object newValue, object oldValue)
        {
            if (ValueChanging != null)
            {
                var e = new CancelExEventArgs(newValue, oldValue);

                //if (EditorCtrl != null)
                //{
                //    EditorCtrl.Invoke(() =>
                //    {
                        ValueChanging(this, e);
                //    });

                //}


                return !e.Cancel;
            }
            else
            {
                return true;
            }




        }

        /// <summary>
        ///     当前字段的值被修改后,调用该函数,引发ValueChaged事件
        /// </summary>
        /// <returns></returns>
        protected virtual void OnValueChanged()
        {
            if (ValueChanged != null)
            {
                ValueChanged(this, EventArgs.Empty);
            }
        }

        protected virtual void OnBindingDataChanged()
        {
            if (BindingDataChanged!=null)
            {
                BindingDataChanged(this, EventArgs.Empty);
            }
        }

        protected virtual void OnVisibleChanged()
        {
            if (VisibleChanged != null)
            {
                VisibleChanged(this, EventArgs.Empty);
            }
        }

        protected virtual void OnEnabledChanged()
        {
            if (EnabledChanged != null)
            {
                EnabledChanged(this, EventArgs.Empty);
            }
        }

        protected virtual void OnLabelTextChanged()
        {
            if (LabelTextChanged != null)
            {
                LabelTextChanged(this, EventArgs.Empty);
            }
        }

        protected virtual void OnIsNullableChanged()
        {
            if (IsNullableChanged != null)
            {
                IsNullableChanged(this, EventArgs.Empty);
            }
        }

        #endregion

        protected virtual object OnGetBindObjectValue()
        {
            if (_bindingObj==null)
            {
                throw new ArgumentNullException("BindingData");
            }

            try
            {
                return _bindingObj.FastGetValue(FieldName);
            }
            catch (Exception ex)
            {
                throw;
            }
            
        }

        protected virtual void OnSetBindObjectValue(object value)
        {
            if (value!=null)
            {
                return;
            }

            _bindingObj.FastSetValue(FieldName, value);
        }

        private void OnBindingPropertyChanged(object sender, PropertyChangedEventArgs e)
        { 
        
        }

        private void OnBindingPropertyChanging(object sender, PropertyChangingEventArgs e)
        {
        
        }

    }


    public class CancelExEventArgs : CancelEventArgs
    {
        public CancelExEventArgs(object newValue = null, object oldValue = null, bool cancel = false)
            : base(cancel)
        {
            NewValue = newValue;
            OldValue = oldValue;
        }

        public object NewValue { get; private set; }

        public object OldValue { get; private set; }
    }
}
